﻿//-----------------------------------------------------------------------
// <copyright file="Field.cs" company="Sergey Daletskiy">
//     Copyright Sergey Daletskiy. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Evolution
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// The evolution field.
    /// </summary>
    public class Field
    {
        /// <summary>
        /// Size of field. Field has square shape.
        /// </summary>
        private int size;

        /// <summary>
        /// Using two buffers to update cells asynchronously. frontBuffer is a buffer that displays on the screen.
        /// </summary>
        private byte[,] frontBuffer;

        /// <summary>
        /// backBuffer is a buffer that stores results of the new iteration, before all cells will be updated.
        /// </summary>
        private byte[,] backBuffer;

        /// <summary>
        /// Initializes a new instance of the Field class with randomly filled cells.
        /// </summary>
        /// <param name="size">size of field</param>
        public Field(int size)
        {
            this.size = size;
            this.frontBuffer = new byte[size, size];
            this.backBuffer = new byte[size, size];

            Random rnd = new Random();

            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    this.frontBuffer[i, j] = (byte)rnd.Next(0, 2);
                }
            }
        }

        /// <summary>
        /// Simply render field to console.
        /// </summary>
        public void RenderToConsole()
        {
            for (int i = 0; i < this.size; i++)
            {
                for (int j = 0; j < this.size; j++)
                {
                    switch (this.frontBuffer[i, j])
                    {
                        case 0:
                            Console.Write(' ');
                            break;
                        case 1:
                            Console.Write('*');
                            break;
                    }
                }

                Console.WriteLine();
            }
        }

        /// <summary>
        /// Updates field with one iteration step.
        /// </summary>
        public void Update()
        {
            for (int i = 0; i < this.size; i++)
            {
                for (int j = 0; j < this.size; j++)
                {
                    int maxNeighbors = 8;

                    if (i == 0 || i == this.size - 1 || j == 0 || j == this.size - 1)
                    {
                        maxNeighbors = 5;
                    }

                    if ((i == 0 || i == this.size - 1) && (j == 0 || j == this.size - 1))
                    {
                        maxNeighbors = 3;
                    }

                    int neighbors = this.GetCellNeighbors(i, j);

                    switch (this.frontBuffer[i, j])
                    {
                        case 0:
                            if (neighbors >= 2)
                            {
                                this.backBuffer[i, j] = 1;
                            }

                            break;
                        case 1:
                            if (neighbors == maxNeighbors)
                            {
                                this.backBuffer[i, j] = 0;
                            }
                            else
                            {
                                this.backBuffer[i, j] = 1;
                            }

                            break;
                    }
                }
            }

            Array.Copy(this.backBuffer, this.frontBuffer, this.backBuffer.Length);
            Array.Clear(this.backBuffer, 0, this.backBuffer.Length);
        }

        /// <summary>
        /// Counts number of cell live neighbors.
        /// </summary>
        /// <param name="x">x-position of cell</param>
        /// <param name="y">y-position of cell</param>
        /// <returns>number of neighbors</returns>
        private int GetCellNeighbors(int x, int y)
        {
            int res = 0;
            int minX = Math.Max(0, x - 1);
            int maxX = Math.Min(this.size - 1, x + 1);
            int minY = Math.Max(0, y - 1);
            int maxY = Math.Min(this.size - 1, y + 1);

            for (int i = minX; i <= maxX; i++)
            {
                for (int j = minY; j <= maxY; j++)
                {
                    if ((i != x || j != y) && this.frontBuffer[i, j] == 1)
                    {
                        res++;
                    }
                }
            }

            return res;
        }
    }
}
